const fs = require("fs").promises;
const path = require("path");
const { glob } = require("glob");

// Dynamic imports for ES modules
let chalk, ora, inquirer;

// Initialize ES modules
async function initializeModules() {
  chalk = (await import("chalk")).default;
  ora = (await import("ora")).default;
  inquirer = (await import("inquirer")).default;
}

class V3ToV4Upgrader {
  constructor() {
    // Constructor remains empty
  }

  async upgrade(options = {}) {
    try {
      // Initialize ES modules
      await initializeModules();
      // Keep readline open throughout the process
      process.stdin.resume();

      // 1. Welcome message
      console.log(
        chalk.bold("\nWelcome to BMad-Method V3 to V4 Upgrade Tool\n")
      );
      console.log(
        "This tool will help you upgrade your BMad-Method V3 project to V4.\n"
      );
      console.log(chalk.cyan("What this tool does:"));
      console.log("- Creates a backup of your V3 files (.bmad-v3-backup/)");
      console.log("- Installs the new V4 .bmad-core structure");
      console.log(
        "- Preserves your PRD, Architecture, and Stories in the new format\n"
      );
      console.log(chalk.yellow("What this tool does NOT do:"));
      console.log(
        "- Modify your document content (use doc-migration-task after upgrade)"
      );
      console.log("- Touch any files outside bmad-agent/ and docs/\n");

      // 2. Get project path
      const projectPath = await this.getProjectPath(options.projectPath);

      // 3. Validate V3 structure
      const validation = await this.validateV3Project(projectPath);
      if (!validation.isValid) {
        console.error(
          chalk.red("\nError: This doesn't appear to be a V3 project.")
        );
        console.error("Expected to find:");
        console.error("- bmad-agent/ directory");
        console.error("- docs/ directory\n");
        console.error(
          "Please check you're in the correct directory and try again."
        );
        return;
      }

      // 4. Pre-flight check
      const analysis = await this.analyzeProject(projectPath);
      await this.showPreflightCheck(analysis, options);

      if (!options.dryRun) {
        const { confirm } = await inquirer.prompt([
          {
            type: "confirm",
            name: "confirm",
            message: "Continue with upgrade?",
            default: true,
          },
        ]);

        if (!confirm) {
          console.log("Upgrade cancelled.");
          return;
        }
      }

      // 5. Create backup
      if (options.backup !== false && !options.dryRun) {
        await this.createBackup(projectPath);
      }

      // 6. Install V4 structure
      if (!options.dryRun) {
        await this.installV4Structure(projectPath);
      }

      // 7. Migrate documents
      if (!options.dryRun) {
        await this.migrateDocuments(projectPath, analysis);
      }

      // 8. Setup IDE
      if (!options.dryRun) {
        await this.setupIDE(projectPath, options.ides);
      }

      // 9. Show completion report
      this.showCompletionReport(projectPath, analysis);

      process.exit(0);
    } catch (error) {
      console.error(chalk.red("\nUpgrade error:"), error.message);
      process.exit(1);
    }
  }

  async getProjectPath(providedPath) {
    if (providedPath) {
      return path.resolve(providedPath);
    }

    const { projectPath } = await inquirer.prompt([
      {
        type: "input",
        name: "projectPath",
        message: "Please enter the path to your V3 project:",
        default: process.cwd(),
      },
    ]);

    return path.resolve(projectPath);
  }

  async validateV3Project(projectPath) {
    const spinner = ora("Validating project structure...").start();

    try {
      const bmadAgentPath = path.join(projectPath, "bmad-agent");
      const docsPath = path.join(projectPath, "docs");

      const hasBmadAgent = await this.pathExists(bmadAgentPath);
      const hasDocs = await this.pathExists(docsPath);

      if (hasBmadAgent) {
        spinner.text = "✓ Found bmad-agent/ directory";
        console.log(chalk.green("\n✓ Found bmad-agent/ directory"));
      }

      if (hasDocs) {
        console.log(chalk.green("✓ Found docs/ directory"));
      }

      const isValid = hasBmadAgent && hasDocs;

      if (isValid) {
        spinner.succeed("This appears to be a valid V3 project");
      } else {
        spinner.fail("Invalid V3 project structure");
      }

      return { isValid, hasBmadAgent, hasDocs };
    } catch (error) {
      spinner.fail("Validation failed");
      throw error;
    }
  }

  async analyzeProject(projectPath) {
    const docsPath = path.join(projectPath, "docs");
    const bmadAgentPath = path.join(projectPath, "bmad-agent");

    // Find PRD
    const prdCandidates = ["prd.md", "PRD.md", "product-requirements.md"];
    let prdFile = null;
    for (const candidate of prdCandidates) {
      const candidatePath = path.join(docsPath, candidate);
      if (await this.pathExists(candidatePath)) {
        prdFile = candidate;
        break;
      }
    }

    // Find Architecture
    const archCandidates = [
      "architecture.md",
      "Architecture.md",
      "technical-architecture.md",
    ];
    let archFile = null;
    for (const candidate of archCandidates) {
      const candidatePath = path.join(docsPath, candidate);
      if (await this.pathExists(candidatePath)) {
        archFile = candidate;
        break;
      }
    }

    // Find Front-end Architecture (V3 specific)
    const frontEndCandidates = [
      "front-end-architecture.md",
      "frontend-architecture.md",
      "ui-architecture.md",
    ];
    let frontEndArchFile = null;
    for (const candidate of frontEndCandidates) {
      const candidatePath = path.join(docsPath, candidate);
      if (await this.pathExists(candidatePath)) {
        frontEndArchFile = candidate;
        break;
      }
    }

    // Find UX/UI spec
    const uxSpecCandidates = [
      "ux-ui-spec.md",
      "ux-ui-specification.md",
      "ui-spec.md",
      "ux-spec.md",
    ];
    let uxSpecFile = null;
    for (const candidate of uxSpecCandidates) {
      const candidatePath = path.join(docsPath, candidate);
      if (await this.pathExists(candidatePath)) {
        uxSpecFile = candidate;
        break;
      }
    }

    // Find v0 prompt or UX prompt
    const uxPromptCandidates = [
      "v0-prompt.md",
      "ux-prompt.md",
      "ui-prompt.md",
      "design-prompt.md",
    ];
    let uxPromptFile = null;
    for (const candidate of uxPromptCandidates) {
      const candidatePath = path.join(docsPath, candidate);
      if (await this.pathExists(candidatePath)) {
        uxPromptFile = candidate;
        break;
      }
    }

    // Find epic files
    const epicFiles = await glob("epic*.md", { cwd: docsPath });

    // Find story files
    const storiesPath = path.join(docsPath, "stories");
    let storyFiles = [];
    if (await this.pathExists(storiesPath)) {
      storyFiles = await glob("*.md", { cwd: storiesPath });
    }

    // Count custom files in bmad-agent
    const bmadAgentFiles = await glob("**/*.md", {
      cwd: bmadAgentPath,
      ignore: ["node_modules/**"],
    });

    return {
      prdFile,
      archFile,
      frontEndArchFile,
      uxSpecFile,
      uxPromptFile,
      epicFiles,
      storyFiles,
      customFileCount: bmadAgentFiles.length,
    };
  }

  async showPreflightCheck(analysis, options) {
    console.log(chalk.bold("\nProject Analysis:"));
    console.log(
      `- PRD found: ${
        analysis.prdFile
          ? `docs/${analysis.prdFile}`
          : chalk.yellow("Not found")
      }`
    );
    console.log(
      `- Architecture found: ${
        analysis.archFile
          ? `docs/${analysis.archFile}`
          : chalk.yellow("Not found")
      }`
    );
    if (analysis.frontEndArchFile) {
      console.log(
        `- Front-end Architecture found: docs/${analysis.frontEndArchFile}`
      );
    }
    console.log(
      `- UX/UI Spec found: ${
        analysis.uxSpecFile
          ? `docs/${analysis.uxSpecFile}`
          : chalk.yellow("Not found")
      }`
    );
    console.log(
      `- UX/Design Prompt found: ${
        analysis.uxPromptFile
          ? `docs/${analysis.uxPromptFile}`
          : chalk.yellow("Not found")
      }`
    );
    console.log(
      `- Epic files found: ${analysis.epicFiles.length} files (epic*.md)`
    );
    console.log(
      `- Stories found: ${analysis.storyFiles.length} files in docs/stories/`
    );
    console.log(`- Custom files in bmad-agent/: ${analysis.customFileCount}`);

    if (!options.dryRun) {
      console.log("\nThe following will be backed up to .bmad-v3-backup/:");
      console.log("- bmad-agent/ (entire directory)");
      console.log("- docs/ (entire directory)");

      if (analysis.epicFiles.length > 0) {
        console.log(
          chalk.green(
            "\nNote: Epic files found! They will be placed in docs/prd/ with an index.md file."
          )
        );
        console.log(
          chalk.green(
            "Since epic files exist, you won't need to shard the PRD after upgrade."
          )
        );
      }
    }
  }

  async createBackup(projectPath) {
    const spinner = ora("Creating backup...").start();

    try {
      const backupPath = path.join(projectPath, ".bmad-v3-backup");

      // Check if backup already exists
      if (await this.pathExists(backupPath)) {
        spinner.fail("Backup directory already exists");
        console.error(
          chalk.red(
            "\nError: Backup directory .bmad-v3-backup/ already exists."
          )
        );
        console.error("\nThis might mean an upgrade was already attempted.");
        console.error(
          "Please remove or rename the existing backup and try again."
        );
        throw new Error("Backup already exists");
      }

      // Create backup directory
      await fs.mkdir(backupPath, { recursive: true });
      spinner.text = "✓ Created .bmad-v3-backup/";
      console.log(chalk.green("\n✓ Created .bmad-v3-backup/"));

      // Move bmad-agent
      const bmadAgentSrc = path.join(projectPath, "bmad-agent");
      const bmadAgentDest = path.join(backupPath, "bmad-agent");
      await fs.rename(bmadAgentSrc, bmadAgentDest);
      console.log(chalk.green("✓ Moved bmad-agent/ to backup"));

      // Move docs
      const docsSrc = path.join(projectPath, "docs");
      const docsDest = path.join(backupPath, "docs");
      await fs.rename(docsSrc, docsDest);
      console.log(chalk.green("✓ Moved docs/ to backup"));

      spinner.succeed("Backup created successfully");
    } catch (error) {
      spinner.fail("Backup failed");
      throw error;
    }
  }

  async installV4Structure(projectPath) {
    const spinner = ora("Installing V4 structure...").start();

    try {
      // Get the source bmad-core directory (without dot prefix)
      const sourcePath = path.join(__dirname, "..", "..", "bmad-core");
      const destPath = path.join(projectPath, ".bmad-core");

      // Copy .bmad-core
      await this.copyDirectory(sourcePath, destPath);
      spinner.text = "✓ Copied fresh .bmad-core/ directory from V4";
      console.log(
        chalk.green("\n✓ Copied fresh .bmad-core/ directory from V4")
      );

      // Create docs directory
      const docsPath = path.join(projectPath, "docs");
      await fs.mkdir(docsPath, { recursive: true });
      console.log(chalk.green("✓ Created new docs/ directory"));

      // Create install manifest for future updates
      await this.createInstallManifest(projectPath);
      console.log(chalk.green("✓ Created install manifest"));

      console.log(
        chalk.yellow(
          "\nNote: Your V3 bmad-agent content has been backed up and NOT migrated."
        )
      );
      console.log(
        chalk.yellow(
          "The new V4 agents are completely different and look for different file structures."
        )
      );

      spinner.succeed("V4 structure installed successfully");
    } catch (error) {
      spinner.fail("V4 installation failed");
      throw error;
    }
  }

  async migrateDocuments(projectPath, analysis) {
    const spinner = ora("Migrating your project documents...").start();

    try {
      const backupDocsPath = path.join(projectPath, ".bmad-v3-backup", "docs");
      const newDocsPath = path.join(projectPath, "docs");
      let copiedCount = 0;

      // Copy PRD
      if (analysis.prdFile) {
        const src = path.join(backupDocsPath, analysis.prdFile);
        const dest = path.join(newDocsPath, analysis.prdFile);
        await fs.copyFile(src, dest);
        console.log(chalk.green(`\n✓ Copied PRD to docs/${analysis.prdFile}`));
        copiedCount++;
      }

      // Copy Architecture
      if (analysis.archFile) {
        const src = path.join(backupDocsPath, analysis.archFile);
        const dest = path.join(newDocsPath, analysis.archFile);
        await fs.copyFile(src, dest);
        console.log(
          chalk.green(`✓ Copied Architecture to docs/${analysis.archFile}`)
        );
        copiedCount++;
      }

      // Copy Front-end Architecture if exists
      if (analysis.frontEndArchFile) {
        const src = path.join(backupDocsPath, analysis.frontEndArchFile);
        const dest = path.join(newDocsPath, analysis.frontEndArchFile);
        await fs.copyFile(src, dest);
        console.log(
          chalk.green(
            `✓ Copied Front-end Architecture to docs/${analysis.frontEndArchFile}`
          )
        );
        console.log(
          chalk.yellow(
            "Note: V4 uses a single full-stack-architecture.md - use doc-migration-task to merge"
          )
        );
        copiedCount++;
      }

      // Copy UX/UI Spec if exists
      if (analysis.uxSpecFile) {
        const src = path.join(backupDocsPath, analysis.uxSpecFile);
        const dest = path.join(newDocsPath, analysis.uxSpecFile);
        await fs.copyFile(src, dest);
        console.log(
          chalk.green(`✓ Copied UX/UI Spec to docs/${analysis.uxSpecFile}`)
        );
        copiedCount++;
      }

      // Copy UX/Design Prompt if exists
      if (analysis.uxPromptFile) {
        const src = path.join(backupDocsPath, analysis.uxPromptFile);
        const dest = path.join(newDocsPath, analysis.uxPromptFile);
        await fs.copyFile(src, dest);
        console.log(
          chalk.green(
            `✓ Copied UX/Design Prompt to docs/${analysis.uxPromptFile}`
          )
        );
        copiedCount++;
      }

      // Copy stories
      if (analysis.storyFiles.length > 0) {
        const storiesDir = path.join(newDocsPath, "stories");
        await fs.mkdir(storiesDir, { recursive: true });

        for (const storyFile of analysis.storyFiles) {
          const src = path.join(backupDocsPath, "stories", storyFile);
          const dest = path.join(storiesDir, storyFile);
          await fs.copyFile(src, dest);
        }
        console.log(
          chalk.green(
            `✓ Copied ${analysis.storyFiles.length} story files to docs/stories/`
          )
        );
        copiedCount += analysis.storyFiles.length;
      }

      // Copy epic files to prd subfolder
      if (analysis.epicFiles.length > 0) {
        const prdDir = path.join(newDocsPath, "prd");
        await fs.mkdir(prdDir, { recursive: true });

        for (const epicFile of analysis.epicFiles) {
          const src = path.join(backupDocsPath, epicFile);
          const dest = path.join(prdDir, epicFile);
          await fs.copyFile(src, dest);
        }
        console.log(
          chalk.green(
            `✓ Found and copied ${analysis.epicFiles.length} epic files to docs/prd/`
          )
        );

        // Create index.md for the prd folder
        await this.createPrdIndex(projectPath, analysis);
        console.log(chalk.green("✓ Created index.md in docs/prd/"));

        console.log(
          chalk.green(
            "\nNote: Epic files detected! These are compatible with V4 and have been copied."
          )
        );
        console.log(
          chalk.green(
            "You won't need to shard the PRD since epics already exist."
          )
        );
        copiedCount += analysis.epicFiles.length;
      }

      spinner.succeed(`Migrated ${copiedCount} documents successfully`);
    } catch (error) {
      spinner.fail("Document migration failed");
      throw error;
    }
  }

  async setupIDE(projectPath, selectedIdes) {
    // Use the IDE selections passed from the installer
    if (!selectedIdes || selectedIdes.length === 0) {
      console.log(chalk.dim("No IDE setup requested - skipping"));
      return;
    }

    const ideSetup = require("../installer/lib/ide-setup");
    const spinner = ora("Setting up IDE rules for all agents...").start();

    try {
      const ideMessages = {
        cursor: "Rules created in .cursor/rules/",
        "claude-code": "Commands created in .claude/commands/",
        windsurf: "Rules created in .windsurf/rules/",
        trae: "Rules created in.trae/rules/",
        roo: "Custom modes created in .roomodes",
        cline: "Rules created in .clinerules/",
      };

      // Setup each selected IDE
      for (const ide of selectedIdes) {
        spinner.text = `Setting up ${ide}...`;
        await ideSetup.setup(ide, projectPath);
        console.log(chalk.green(`\n✓ ${ideMessages[ide]}`));
      }

      spinner.succeed(`IDE setup complete for ${selectedIdes.length} IDE(s)!`);
    } catch (error) {
      spinner.fail("IDE setup failed");
      console.error(
        chalk.yellow("IDE setup failed, but upgrade is complete.")
      );
    }
  }

  showCompletionReport(projectPath, analysis) {
    console.log(chalk.bold.green("\n✓ Upgrade Complete!\n"));
    console.log(chalk.bold("Summary:"));
    console.log(`- V3 files backed up to: .bmad-v3-backup/`);
    console.log(`- V4 structure installed: .bmad-core/ (fresh from V4)`);

    const totalDocs =
      (analysis.prdFile ? 1 : 0) +
      (analysis.archFile ? 1 : 0) +
      (analysis.frontEndArchFile ? 1 : 0) +
      (analysis.uxSpecFile ? 1 : 0) +
      (analysis.uxPromptFile ? 1 : 0) +
      analysis.storyFiles.length;
    console.log(
      `- Documents migrated: ${totalDocs} files${
        analysis.epicFiles.length > 0
          ? ` + ${analysis.epicFiles.length} epics`
          : ""
      }`
    );

    console.log(chalk.bold("\nImportant Changes:"));
    console.log(
      "- The V4 agents (sm, dev, etc.) expect different file structures than V3"
    );
    console.log(
      "- Your V3 bmad-agent content was NOT migrated (it's incompatible)"
    );
    if (analysis.epicFiles.length > 0) {
      console.log(
        "- Epic files were found and copied - no PRD sharding needed!"
      );
    }
    if (analysis.frontEndArchFile) {
      console.log(
        "- Front-end architecture found - V4 uses full-stack-architecture.md, migration needed"
      );
    }
    if (analysis.uxSpecFile || analysis.uxPromptFile) {
      console.log(
        "- UX/UI design files found and copied - ready for use with V4"
      );
    }

    console.log(chalk.bold("\nNext Steps:"));
    console.log("1. Review your documents in the new docs/ folder");
    console.log(
      "2. Use @bmad-master agent to run the doc-migration-task to align your documents with V4 templates"
    );
    if (analysis.epicFiles.length === 0) {
      console.log(
        "3. Use @bmad-master agent to shard the PRD to create epic files"
      );
    }

    console.log(
      chalk.dim(
        "\nYour V3 backup is preserved in .bmad-v3-backup/ and can be restored if needed."
      )
    );
  }

  async pathExists(filePath) {
    try {
      await fs.access(filePath);
      return true;
    } catch {
      return false;
    }
  }

  async copyDirectory(src, dest) {
    await fs.mkdir(dest, { recursive: true });
    const entries = await fs.readdir(src, { withFileTypes: true });

    for (const entry of entries) {
      const srcPath = path.join(src, entry.name);
      const destPath = path.join(dest, entry.name);

      if (entry.isDirectory()) {
        await this.copyDirectory(srcPath, destPath);
      } else {
        await fs.copyFile(srcPath, destPath);
      }
    }
  }

  async createPrdIndex(projectPath, analysis) {
    const prdIndexPath = path.join(projectPath, "docs", "prd", "index.md");
    const prdPath = path.join(
      projectPath,
      "docs",
      analysis.prdFile || "prd.md"
    );

    let indexContent = "# Product Requirements Document\n\n";

    // Try to read the PRD to get the title and intro content
    if (analysis.prdFile && (await this.pathExists(prdPath))) {
      try {
        const prdContent = await fs.readFile(prdPath, "utf8");
        const lines = prdContent.split("\n");

        // Find the first heading
        const titleMatch = lines.find((line) => line.startsWith("# "));
        if (titleMatch) {
          indexContent = titleMatch + "\n\n";
        }

        // Get any content before the first ## section
        let introContent = "";
        let foundFirstSection = false;
        for (const line of lines) {
          if (line.startsWith("## ")) {
            foundFirstSection = true;
            break;
          }
          if (!line.startsWith("# ")) {
            introContent += line + "\n";
          }
        }

        if (introContent.trim()) {
          indexContent += introContent.trim() + "\n\n";
        }
      } catch (error) {
        // If we can't read the PRD, just use default content
      }
    }

    // Add sections list
    indexContent += "## Sections\n\n";

    // Sort epic files for consistent ordering
    const sortedEpics = [...analysis.epicFiles].sort();

    for (const epicFile of sortedEpics) {
      // Extract epic name from filename
      const epicName = epicFile
        .replace(/\.md$/, "")
        .replace(/^epic-?/i, "")
        .replace(/-/g, " ")
        .replace(/^\d+\s*/, "") // Remove leading numbers
        .trim();

      const displayName = epicName.charAt(0).toUpperCase() + epicName.slice(1);
      indexContent += `- [${
        displayName || epicFile.replace(".md", "")
      }](./${epicFile})\n`;
    }

    await fs.writeFile(prdIndexPath, indexContent);
  }

  async createInstallManifest(projectPath) {
    const fileManager = require("../installer/lib/file-manager");
    const { glob } = require("glob");

    // Get all files in .bmad-core for the manifest
    const bmadCorePath = path.join(projectPath, ".bmad-core");
    const files = await glob("**/*", {
      cwd: bmadCorePath,
      nodir: true,
      ignore: ["**/.git/**", "**/node_modules/**"],
    });

    // Prepend .bmad-core/ to file paths for manifest
    const manifestFiles = files.map((file) => path.join(".bmad-core", file));

    const config = {
      installType: "full",
      agent: null,
      ide: null, // Will be set if IDE setup is done later
    };

    await fileManager.createManifest(projectPath, config, manifestFiles);
  }
}

module.exports = V3ToV4Upgrader;
